﻿/**
 * Create in 2012-2-3.
 */
package kit4j.generator.keyless;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;

/**
 * 随机汉字生成器。生成的汉字仅限于GB2312编码规范中定义的第一集常用汉字。<br />
 * 
 * 关于GB2312编码<br />
 * GB2312是基于区位码设计的，区位码把编码表分为94个区，每个区对应94个位，每个字符的区号和位号组合起来就是该汉字的区位码。区位码一般
 * 用10进制数来表示，如1601就表示16区1位，对应的字符是“啊”。在区位码的区号和位号上分别加上0xA0就得到了GB2312编码。 区位码中01-09区是符号
 * 、数字区，16-87区是汉字区，10-15和88-94是未定义的空白区。它将收录的汉字分成两级：第一级是常用汉字计3755个，置于16
 * -55区，按汉语拼音字母/笔形顺序排列；第二级汉字是次常用汉字计3008个，置于56-87区，按部首/笔画顺序排列。
 * 
 * @author MiniKnife
 * 
 */
public class RandomChineseCharacterKeylessGenerator implements IKeylessGenerator<Character> {

	/**
	 * 位起始值。
	 */
	private static final byte codingStartingValue = (byte) 0xA0;

	/**
	 * 区位编号最低值。
	 */
	private static final byte zoneMinNumber = 16;

	/**
	 * 区位编号最高值。
	 */
	private static final byte zoneMaxNumber = 55;

	/**
	 * 每个区中位的个数。
	 */
	private static final byte zoneBitCount = 94;

	private static Charset charset = createCharset();

	private static Exception initCharsetException;
	
	private kit4j.generator.keyed.RandomByteKeyedGenerator numberGenerator;

	private static Charset createCharset() {
		try {
			return Charset.forName("GB2312");
		} catch (Exception e) {
			initCharsetException = e;
			
			return null;
		}
	}

	/**
	 * 创建一个随机GB2312汉字生成器。
	 */
	public RandomChineseCharacterKeylessGenerator() throws Exception {
		checkCharset();

		numberGenerator = new kit4j.generator.keyed.RandomByteKeyedGenerator();
	}

	private void checkCharset() throws Exception {
		if (initCharsetException != null) {
			throw initCharsetException;
		}
	}

	/**
	 * 返回下一个随机汉字。
	 * 
	 * @see kit4j.generator.keyless.IKeylessGenerator#next()
	 */
	@Override
	public Character next() {
		byte firstByte = (byte) (numberGenerator.next((byte) (zoneMaxNumber - zoneMinNumber + 1)) + zoneMinNumber + codingStartingValue);
		byte secondByte;

		/*
		 * 0xD7是GB2312编码汉字区55区的区位码，这个区的最后6个位是留空的，没有对应编码的汉字，所以在这里判断如果区码是0xD7，区位数量要少6个。
		 */
		if (firstByte == (byte) 0xD7) {
			secondByte = (byte) (numberGenerator.next((byte) (zoneBitCount - 6)) + 1 + codingStartingValue);
		} else {
			secondByte = (byte) (numberGenerator.next(zoneBitCount) + 1 + codingStartingValue);
		}

		ByteBuffer buf = ByteBuffer.allocate(2);

		buf.put(firstByte).put(secondByte);

		buf.rewind();

		return charset.decode(buf).get();
	}

}
